package com.asm.server;

import com.asm.message.PingMessage;
import com.asm.protocol.MessageCodecSharable;
import com.asm.protocol.ProcotolFrameDecoder;
import com.asm.server.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * 聊天--服务端
 */
@Slf4j
public class ChatServer {



    public static void main(String[] args) throws InterruptedException {

        final NioEventLoopGroup boss = new NioEventLoopGroup(1); // 内部1个NioEventLoop
        final NioEventLoopGroup worker = new NioEventLoopGroup(); // 内部8个NioEventLoop
        // 局部变量
        MessageCodecSharable       MESSAGE_CODEC = new MessageCodecSharable();
        LoggingHandler           LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
        LoginRequestMessageHandler LOGIN_HANDLER = new LoginRequestMessageHandler();   //--登录---处理器
        final ChatRequestMessageHandler CHAT_HANDLER = new ChatRequestMessageHandler();//--单聊---处理器
        final GroupCreateRequestMessageHandler GROUP_CREATE_HANDLER = new GroupCreateRequestMessageHandler();//--创建群聊---处理器
        final GroupChatRequestMessageHandler GROUP_CHAT_HANDLER = new GroupChatRequestMessageHandler();      //--群聊---处理器
        final GroupMembersRequestHandler GROUP_MEMBERS_HANDLER = new GroupMembersRequestHandler();
        final QuitHandler QUIT_HANDLER = new QuitHandler();  //--断开连接---处理器


        try {
            final ServerBootstrap bs = new ServerBootstrap();
            bs.channel(NioServerSocketChannel.class); // 设置channelFactory创建Channel的类型
            bs.group(boss, worker); // 设置reactor模式对应的事件循环组
            // 调整服务器端 滑动窗口大小，一般不用设置，在建立连接后系统自动设置，netty默认的是1024
            // bs.option(ChannelOption.SO_RCVBUF, 10);
            // 调整 netty 的接收缓冲区大小（ByteBuf） AdaptiveRecvByteBufAllocator(最小值, 初始值, 最大值)
            // AbstractNioByteChannel.read方法中，读取内核数据时，暂时创建的用户缓冲区byteBuf的大小，
            // 默认使用PooledByteBufAllocator，byteBuf类型为PooledUnsafeDirectByteBuf，大小1024
            //bs.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(16, 16, 16));
            // childHandler会在新的NioSocketChannel注册到childGroup中某个NioEventLoop.selector后被
            // 加入NioSocketChannel.pipeline中，并回调initChannel方法
            bs.childHandler(new ChannelInitializer<SocketChannel>() { // 设置ChannelHandler childHandler

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                // 入站&出站：连接保活，心跳检测，IdleStateHandler监视+userEventTriggered回调
                // 用来判断 是不是读 空闲时间过长，或写空闲时间过长 (读，写，读写空闲时间限制) 0表示不关心
                ch.pipeline().addLast(new IdleStateHandler(
                        12,
                        0,
                        0));
                /*
                ################################################################
                #####  ChannelDuplexHandler 可以同时作为 入站 和 出站处理器  #######
                ##### 12 秒内 没读到数据 触发   IdleState.READER_IDLE       #######
                #####       写         触发   IdleState.WRITER_IDLE       #######
                #####     读写         触发   IdleState.ALL_IDLE          #######
                ################################################################
                 */
                ch.pipeline().addLast(new IdleStateEventHandler()); // 空闲事件处理handler一定要写在IdleStateHandler后面，事件向后触发
                // 入站：处理连接关闭和异常发生情况
                ch.pipeline().addLast(QUIT_HANDLER);               //--断开连接---处理器
                // 入站：对协议包解码，得到
                ch.pipeline().addLast(new ProcotolFrameDecoder()); // 帧解码器 【与自定义编解码器 MessageCodecSharable一起配置参数】
                // 入站&出站，打印入站数据和出站数据，以及各种事件日志
                ch.pipeline().addLast(LOGGING_HANDLER);            // 日志
                // 入站&出站：对象和字节数组之间相互转换
                ch.pipeline().addLast(MESSAGE_CODEC);              // 出站入站的 自定义编解码器 【 解析消息类型 】
                // simple处理器 【针对性的对登录进行处理】 【流水线 会向上执行出站Handler,  到 ProcotolFrameDecoder(入站停止)】
                ch.pipeline().addLast(LOGIN_HANDLER);         //--登录---处理器
                ch.pipeline().addLast(CHAT_HANDLER);          //--单聊---处理器
                ch.pipeline().addLast(GROUP_CREATE_HANDLER);  //--创建群聊---处理器
                ch.pipeline().addLast(GROUP_CHAT_HANDLER);    //--群聊---处理器
                ch.pipeline().addLast(GROUP_MEMBERS_HANDLER); // 群聊---查看群成员列表

                ch.pipeline().addLast(new LifeCycleTestHandlerInbound()); // 测试InboundHandler生命周期
                ch.pipeline().addLast(new LifeCycleTestHandlerOutbound()); // 测试OutboundHandler生命周期
                ch.pipeline().addLast(new LifeCycleTestHandlerDuplex()); // 测试DuplexHandler生命周期
                }
            });

            ChannelFuture channelFuture = bs.bind(8080).sync();
            channelFuture.channel().closeFuture().sync();

        } catch (InterruptedException e) {

            log.error("server error", e);

        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }


}
